Serout (send data)

transmits a byte or string of bytes serially at 300 to 2400 bps (4 MHz clock).

Serout lets a PIC application transmit data to another PIC or any computer equipped with an RS-232 serial port.

If you have not already done so, please read the discussion in paragraph two of the first Serin entry (page 74) on the meaning of "polarity" as it applies to these serial routines.

When you want to connect the PIC's serial output directly to a computer without a line driver, use inverted polarity. Even though the voltage swing isn't legal RS-232, this will generally work, provided cables are kept short.

To use Serout, put the desired pin into the stop-bit state (0 for inverted output, 1 for true) and make the pin an output. This will prevent sending a false start bit to the receiver.

Next, create a string of the bytes you want to transmit at the location labeled buffer. Store the number of bytes at buffer, the first byte at buffer-1, the second byte at buffer-2, and so on.

Specify the baud rate by moving one of the constants B300 through B2400 into the variable baud. Specify the sense by setting or clearing the polarity bit (0 = true, 1 = inverted). Put the pin number into pin and the port number into port. Call Serout. The routine will automatically send each byte of the string.

When Serout returns, the data starting at buffer-1 will be intact, but the count (at buffer) will have been cleared to 0.

Note that Serout permits your program to make a serious mistake; If the count at buffer is 0 when Serout is called, the routine will attempt to send 256 bytes of data. It will alter ports and control registers in the lower seven or eight bytes of RAM. Make sure that buffer contains the correct count before calling Serout.

If your application requires sending a number out the serial port as a text string, see the next entry, Serout (format # data). It converts a 16-bit value into a Serout string with an optional fixed decimal point.

Note that this version of Serout does not support the open-drain/open-source capabilities of the PBASIC version. Those require manipulation of the TRIS register, which we've avoided in this series of subroutines.

Serout can transmit serial data through any pin of any I/O port, and can change port and pin assignments while the program is running. In many applications, it's enough to assign one pin for serial output and leave it at that. The disk that accompanies this book contains stripped versions of Serout for such applications. The advantage is that they use less program memory than the routine listed here. Check the files SEROUT_2.SRC and SEROUT_3.SRC on the accompanying disk.

Serout, like the other routines that accept pin and port arguments, requires the short table Pinz. Remember that tables and subroutines must be located in the first 256 words of a 512-word program memory page.

Demonstrating Serout.

To see Serout in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run SEROUT.SRC. Make sure to use a 4-MHz clock on the PIC or downloader. Load a terminal program on the PC connected to the PIC and set it for 2400 baud, no parity, 8 data bits, 1 stop bit (N81). When you apply power to the PIC or reset the downloader, the letters "OK" will appear on the PC screen.


;
; ***************************************************************************
; ***  Bubble Software Parallax to PIC Source Converter. Copyright 1999.  ***
; ***  http://www.bubblesoftonline.com                 email: sales@picnpoke.com  ***
; ***************************************************************************
;
; SEROUT port (in w), pin, baud, polarity, data
; Transmits data serially at the specified rate and polarity out the specified port
; pin. Data bytes to be transmitted must be arranged in memory starting at the
; address "buffer-1" and working downward (e.g., 30,29,28...). The number of
; bytes to transmit must be stored at the address labeled "buffer." A companion
; routine, BIN_ASC, converts 16-bit numbers to this form, optionally adding a
; decimal point. See the listing for Serout ...(format # data).
; Serout uses the common data format of no parity, 8 data
; bits, 1 stop bit (N81).

; Device data and reset vector
	P = pic16c55
	#include <16c55.inc>   ; processor assembler definitions
	_CONFIG _xt_osc & _wdt_off & _protect_off
 reset start

buffer 	equ 	d'31' 	; String storage at end of memory.
B2400 	equ 	d'30' 	; Bit delay for 2400 baud at 4MHz.
B1200 	equ 	d'62' 	; Bit delay for 1200 baud at 4MHz.
B600 	equ  	d'126'	; Bit delay for 600 baud at 4MHz.
B300 	equ  	d'254'	; Bit delay for 300 baud at 4MHz.

	 org 8
baud 	Res 	d'1' 	; Baud rate.
pin 	Res 	d'1' 	; Pin number (0-7).
port 	Res 	d'1' 	; Port number (0-2).
temp1 	Res 	d'1' 	; Temporary counter for Serout.
temp2 	Res 	d'1' 	; Temporary counter for Serout.
temp3 	Res 	d'1' 	; Temporary counter for delay.

polarity equ	PA2	; Polarity: 0=true, 1=inverted.
; Note that the polarity flag is the unused page-address bit of the PIC 16C5x
; series. If you port this program to another type of PIC, or another routine uses
; this bit, you may have to reassign it.

	org 0

; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b).
Pinz         ADDWF pcl                  
             RETLW d'1'                 
             RETLW d'2'
             RETLW d'4'
             RETLW d'8'
             RETLW d'16'
             RETLW d'32'
             RETLW d'64'
             RETLW d'128'

; Subroutine used by Serout to send a bit with the specified polarity (true or
; inverted) and timing (300, 600, 1200, or 2400 bps).
emitBit      MOVF port,w                ; Point to the output port.
             MOVWF fsr
             MOVF baud,w                ; Load baud rate into temp counter.
             MOVWF temp3
             MOVF status,w              ; Copy carry bit (data bit) into w.
             BTFSC status,pa2           ; If polarity = 1 then invert w.0.
             XORLW d'1'                 
             ANDLW d'1'                 ; Copy w.0 back to carry bit.
             BCF status,c               ; (This roundabout method of inverting
             BTFSC status,z             ; carry is needed since "XOR status,w"
             BSF status,c               ; doesn't work properly.)

; This loop deserves some explanation. The first time through, it sets or clears
; the serial output bit. Thereafter, it's just a delay loop.
emitBit_loop MOVF pin,w                 ; Get the pin mask.
             BTFSS status,c             ; Data in carry bit.
             IORWF indirect             ; If carry = 0 then set bit.
             COMF pin,w                 
             BTFSC status,c             
             ANDWF indirect             ; Else if carry = 1 then clear bit.
             GOTO $+1                   ; Two-cycle nops for time delay.
             GOTO $+1                   
             DECFSZ temp3               ; Number of trips through loop
             GOTO emitBit_loop
             RETLW 0h                   ; set by baud rate in temp3.

; Start of demo program. Upon power up or reset, the PIC transmits the string
; "OK" via pin 2 of port ra at 2400 baud, inverted polarity

start        CLRF 5h                    ; High is a start bit, so hold ra low.
             MOVLW d'0'                 ; All outputs.
             TRIS 5h
             MOVLW d'2'                 ; Put test string "OK"
             MOVWF buffer
             MOVLW 'O'                  ; into the buffer, starting with the
             MOVWF buffer-1
             MOVLW 'K'                  ; number of characters.
             MOVWF buffer-2
             BSF status,pa2             ; Inverted for direct connection.
             MOVLW B2400                ; 2400 baud.
             MOVWF baud
             MOVLW d'2'                 ; Pin 2.
             MOVWF pin
             MOVLW d'0'                 ; of port ra.
             MOVWF port
             CALL Serout                ; Send the data.
             GOTO $                     ; Endless loop

; Upon entry, the desired pin must already be set up as an output.
; The w register contains a number representing the output port (0-2) for
; RA through RC. Variable "pin" contains the pin number (0-7). The
; variable baud must contain a number representing the baud rate.
; The polarity flag must be set (1) for true, or cleared (0) for inverted
; output. A data string consisting of the number of bytes, followed by the
; bytes arranged downward in memory, must be at the location "buffer."
; Upon return, the data in the buffer will be unchanged, but the 0th entry
; (representing the string length) will have been decremented to 0.

Serout       MOVLW 5h                   ; Add offset for port RA.
             ADDWF port
             MOVF pin,w                 
             CALL Pinz                  ; Get bit mask from the table.
             MOVWF pin                  ; Put the mask into pin.
             MOVLW buffer-1             ; Point to first data address.
             MOVWF temp1
Serout_xmit  BCF status,c               ; Start bit into carry
             CALL emitBit               ; Send start bit.
             MOVLW d'8'                 ; Send 8 data bits.
             MOVWF temp2
Serout_bits  MOVF temp1,w               ; Point to data byte.
             MOVWF fsr
             RRF indirect               ; Rotate bit 0 into carry.
             CALL emitBit               ; Send the data bit.
             DECFSZ temp2               ; All 8 bits sent? No: send more bits.
             GOTO Serout_bits
             MOVF temp1,w               ; Point to the data byte.
             MOVWF fsr
             RRF indirect               ; Rotate it back into original position.
             BTFSS status,pa2           ; If it was inverted, invert it again to
             COMF indirect              ; restore original.
             BSF status,c               ; Move stop bit into carry
             CALL emitBit               ; Send the stop bit.
             DECF temp1                 ; Point to next data byte.
             DECFSZ buffer              ; All bytes sent? No: send more bytes.
             GOTO Serout_xmit
             RETLW 0h                   ; Yes: return.
             
             
             end



; SEROUT port (in w), pin, baud, polarity, data
; Transmits data serially at the specified rate and polarity out the specified port
; pin. Data bytes to be transmitted must be arranged in memory starting at the
; address "buffer-1" and working downward (e.g., 30,29,28...). The number of
; bytes to transmit must be stored at the address labeled "buffer." A companion
; routine, BIN_ASC, converts 16-bit numbers to this form, optionally adding a
; decimal point. See the listing for Serout ...(format # data).
; Serout uses the common data format of no parity, 8 data
; bits, 1 stop bit (N81).

buffer	=	31	; String storage at end of memory.
B2400	=	30	; Bit delay for 2400 baud at 4MHz.
B1200	=	62	; Bit delay for 1200 baud at 4MHz.
B600	=	126	; Bit delay for 600 baud at 4MHz.
B300	=	254	; Bit delay for 300 baud at 4MHz.

	org	8
baud	ds	1	; Baud rate.
pin	ds	1	; Pin number (0-7).
port	ds	1	; Port number (0-2).
temp1	ds	1	; Temporary counter for Serout.
temp2	ds	1	; Temporary counter for Serout.
temp3	ds	1	; Temporary counter for delay.
polarity	=	PA2	; Polarity: 0=true, 1=inverted.
; Note that the polarity flag is the unused page-address bit of the PIC 16C5x
; series. If you port this program to another type of PIC, or another routine uses
; this bit, you may have to reassign it.

; Device data and reset vector
	device	pic16c55,xt_osc,wdt_off,protect_off
	reset	start
	org	0

; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b).
Pinz	jmp	pc+w
	retw	1,2,4,8,16,32,64,128

; Subroutine used by Serout to send a bit with the specified polarity (true or
; inverted) and timing (300, 600, 1200, or 2400 bps).
emitBit mov	fsr,port	; Point to the output port.
	mov	temp3,baud	; Load baud rate into temp counter.
	mov	w,status	; Copy carry bit (data bit) into w.
	snb	polarity	; If polarity = 1 then invert w.0.
	XOR	w,#1
	AND	w,#1	; Copy w.0 back to carry bit.
	clc	; (This roundabout method of inverting
	snz	; carry is needed since "XOR status,w"
	stc	; doesn't work properly.)

; This loop deserves some explanation. The first time through, it sets or clears
; the serial output bit. Thereafter, it's just a delay loop.
:loop	mov	w,pin	; Get the pin mask.
	sc	; Data in carry bit.
	OR	indirect,w	; If carry = 0 then set bit.
	mov	w,/pin
	snc
	AND	indirect,w	; Else if carry = 1 then clear bit.
	jmp	$+1	; Two-cycle nops for time delay.
	jmp	$+1
	djnz	temp3,:loop	; Number of trips through loop
	ret	; set by baud rate in temp3.

; Start of demo program. Upon power up or reset, the PIC transmits the string
; "OK" via pin 2 of port ra at 2400 baud, inverted polarity

start	clr	ra	; High is a start bit, so hold ra low.
	mov	!ra, #0 ; All outputs.
	mov	buffer,#2	; Put test string "OK"
	mov	buffer-1,#'O'	; into the buffer, starting with the
	mov	buffer-2,#'K'	; number of characters.
	setb	polarity	; Inverted for direct connection.
	mov	baud,#B2400	; 2400 baud.
	mov	pin,#2	; Pin 2.
	mov	port,#0 ; of port ra.
	call	Serout	; Send the data.
	jmp	$	; Endless loop

; Upon entry, the desired pin must already be set up as an output.
; The w register contains a number representing the output port (0-2) for
; RA through RC. Variable "pin" contains the pin number (0-7). The
; variable baud must contain a number representing the baud rate.
; The polarity flag must be set (1) for true, or cleared (0) for inverted
; output. A data string consisting of the number of bytes, followed by the
; bytes arranged downward in memory, must be at the location "buffer."
; Upon return, the data in the buffer will be unchanged, but the 0th entry
; (representing the string length) will have been decremented to 0.

Serout	add	port,#RA	; Add offset for port RA.
	mov	w,pin
	call	Pinz	; Get bit mask from the table.
	mov	pin,w	; Put the mask into pin.
	mov	temp1,#buffer-1 ; Point to first data address.
:xmit	clc	; Start bit into carry
	call	emitBit ; Send start bit.
	mov	temp2,#8	; Send 8 data bits.
:bits	mov	fsr,temp1	; Point to data byte.
	rr	indirect	; Rotate bit 0 into carry.
	call	emitBit ; Send the data bit.
	djnz	temp2,:bits	; All 8 bits sent? No: send more bits.
	mov	fsr,temp1	; Point to the data byte.
	rr	indirect	; Rotate it back into original position.
	sb	polarity	; If it was inverted, invert it again to
	NOT	indirect	; restore original.
	stc	; Move stop bit into carry
	call	emitBit ; Send the stop bit.
	dec	temp1	; Point to next data byte.
	djnz	buffer,:xmit	; All bytes sent? No: send more bytes.
	ret	; Yes: return.

See also: